package com.springboot.demo.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.cx.item.common.exception.ParamsCheckException;
import com.cx.item.common.utils.DateUtil;
import com.cx.item.common.utils.StringUtil;
import com.cx.item.common.utils.TokenUtil;
import com.cx.item.common.wechat.wxpay.model.MiniappPayConfig;
import com.cx.item.common.wechat.wxpay.service.WechatAbstractFactory;
import com.cx.item.common.wechat.wxpay.service.WechatAbstractService;
import com.cx.item.common.wechat.wxpay.service.WechatNotifyAbstract;
import com.springboot.demo.model.constants.SunMiniappRedisConstants;
import com.springboot.demo.model.enums.GoodsTypeEnum;
import com.springboot.demo.model.model.Customer;
import com.springboot.demo.model.model.Order;
import com.springboot.demo.model.par.WechatPayPar;
import com.springboot.demo.model.vo.WechatLoginVo;
import com.springboot.demo.service.CustomerService;
import com.springboot.demo.service.OrderService;
import com.springboot.demo.service.WechatService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * Created by hwm on 2018/6/27.
 */
@Slf4j
@Service
public class WechatServiceImpl implements WechatNotifyAbstract, WechatService {

    @Value("${miniapp.key}")
    private String miniappKey;

    @Value("${miniapp.mch_id}")
    private String miniappMch_id;

    @Value("${miniapp.appid}")
    private String miniappAppid;

    @Value("${miniapp.appsecret}")
    private String miniappAppsecret;

    @Value("${miniapp.notify_url}")
    private String miniappNotify_url;

    @Value("${image.host.prefix}")
    private String imageHostPrefix;

    @Autowired
    private CustomerService customerService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public WechatLoginVo wechatLogin(String code) {

        Map<String, String> sessionKeyAndOpenid = WechatAbstractService.getSessionKeyAndOpenid(code, miniappAppid, miniappAppsecret);
        if (CollUtil.isEmpty(sessionKeyAndOpenid)) {
            throw new ParamsCheckException("code有误,数据不存在");
        }

        String openid = sessionKeyAndOpenid.get("openid");
        String session_key = sessionKeyAndOpenid.get("session_key");

        // redis是否有openid(有效时间一秒)，防止前端重复并发请求
        // 临时避免重复登录key
        String cacheTempKey = StrUtil.format("wechatLogin_{}", openid);
        // 缓存openid到redis
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(cacheTempKey, openid);
        if (aBoolean) {
            // 设置成功，设置key缓存时间，有效时间一秒
            try {
                redisTemplate.expire(cacheTempKey, 1, TimeUnit.SECONDS);
            } catch (Exception e) {
                redisTemplate.delete(cacheTempKey);
            }
        } else {
            log.debug("============================并发登录，不予处理===========================");
            log.debug("============================并发登录，不予处理===========================");
            log.debug("============================并发登录，不予处理===========================");
            throw new ParamsCheckException("请不要重复登录");
        }

        Customer customer = customerService.getCustomerByOpenId(openid);

        WechatLoginVo wechatLoginVo = new WechatLoginVo();
        if (customer == null) {
            // 新用户,添加用户信息
            customer = addCustomer(openid, session_key);

            // 新用户
            wechatLoginVo.setCustomerType(1);
        } else {
            // 从redis删除老的的token
            log.debug("=======================================>开始删除老的token=" + customer.getCustomerToken());
            Long delete = redisTemplate.opsForHash().delete(SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY, customer.getCustomerToken());
            log.debug("<======================================删除token数量=" + delete);

            // 重新生成token,返回用户信息
            customer.setCustomerToken(createCustomerToken(customer.getOpenId(), customer.getSessionKey()));
            customer.setSessionKey(session_key);
            customer.setCustomerStatus(1);
            customer.setUpdateTime(new Date());
            customerService.updateByPrimaryKeySelective(customer);

            // 老用户
            wechatLoginVo.setCustomerType(2);
        }
        BeanUtil.copyProperties(customer, wechatLoginVo);

        // 添加token到redis token列表
        String val = StrUtil.format("{}_{}", String.valueOf(customer.getCustomerId()), cn.hutool.core.date.DateUtil.formatDateTime(new Date()));
        log.debug("======================================添加新用户token到缓存=" + customer.getCustomerToken());
        redisTemplate.opsForHash().put(SunMiniappRedisConstants.CUSTOMER_CACHE_REDIS_TOKEN_KEY, customer.getCustomerToken(), val);


        if (StrUtil.isNotEmpty(wechatLoginVo.getHeadImg()) && !wechatLoginVo.getHeadImg().startsWith("http")) {
            wechatLoginVo.setHeadImg(StrUtil.format("{}/{}", imageHostPrefix, wechatLoginVo.getHeadImg()));
        }

        return wechatLoginVo;
    }

    private Customer addCustomer(String openid, String session_key) {

        Customer customerMod = new Customer();
        customerMod.setOpenId(openid);
        customerMod.setSessionKey(session_key);
        customerMod.setDataPercent("0%");
        customerMod.setCustomerStatus(1);
        customerMod.setCustomerToken(createCustomerToken(openid, session_key));
        customerMod.setCreateTime(new Date());
        customerService.insertSelective(customerMod);

        return customerMod;
    }

    /**
     * 根据openid和session_key生成用户token
     *
     * @param openid
     * @param session_key
     * @return
     */
    private String createCustomerToken(String openid, String session_key) {

        StringBuilder sb = new StringBuilder();
        sb.append(openid);
        sb.append(session_key);
        sb.append(DateUtil.getDateFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));
        sb.append(StringUtil.getStringRandom(10));

        return TokenUtil.generateMD5(sb.toString());
    }

    @Override
    public void wechatNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {

        MiniappPayConfig miniappPayConfig = new MiniappPayConfig();
        miniappPayConfig.setKey(miniappKey);
        miniappPayConfig.setRequest(request);
        miniappPayConfig.setResponse(response);
        WechatAbstractService miniappPayInstance = WechatAbstractFactory.getMiniappPayInstance(miniappPayConfig);
        miniappPayInstance.wechatNotify(this);

    }

    @Override
    public Map<String, String> wechatPay(WechatPayPar wechatPayPar, HttpServletRequest request) {

        Order order = orderService.selectByPrimaryKey(wechatPayPar.getOrderId());
        if (order == null) {
            throw new ParamsCheckException("订单不存在");
        }
        // 支付状态:1待确认,2待付款,3服务中,4已完成
        if (order.getPayStatus() >= 3) {
            throw new ParamsCheckException("请不要重复支付");
        }

        Customer customerM = orderService.getCustomerByOrderId(wechatPayPar.getOrderId());
        if (customerM == null) {
            throw new ParamsCheckException("订单对应用户不存在");
        }

        String openid = customerM.getOpenId();
        String body = StrUtil.format("购买商品{}花费{}元", order.getGoodsName(), wechatPayPar.getPayPrice());
        String outTradeNo = order.getOrderNumber();
        String payPrice = wechatPayPar.getPayPrice();
        MiniappPayConfig miniappPayConfig = new MiniappPayConfig(openid, miniappKey, miniappAppid, miniappMch_id, miniappNotify_url, payPrice, body, outTradeNo, request);
        WechatAbstractService miniappPayInstance = WechatAbstractFactory.getMiniappPayInstance(miniappPayConfig);
        Map<String, String> map = miniappPayInstance.wechatPay();
        if (CollUtil.isEmpty(map)) {
            throw new ParamsCheckException("支付有误，请联系系统管理员");
        }

        // 更新订单支付价格
        order.setPayPrice(new BigDecimal(wechatPayPar.getPayPrice()));
        order.setUpdateTime(new Date());
        order.setRemark(StrUtil.format("更新订单支付价格为{}元", wechatPayPar.getPayPrice()));
        orderService.updateByPrimaryKeySelective(order);

        return map;
    }

    @Override
    public void wechatNotifyService(Map<String, Objects> notifyMap) {

        log.debug("<====微信回调具体参数" + notifyMap.toString());
        String out_trade_no = String.valueOf(notifyMap.get("out_trade_no")); // 订单号
        Order orderUp = orderService.getOrderByOrderNumber(out_trade_no);
        if (orderUp == null) {
            throw new ParamsCheckException(StrUtil.format("订单{}不存在", out_trade_no));
        }

        // 更新订单状态支付状态:1待确认,2待付款,3服务中,4已完成
        // 专业保洁付款变成已完成，月嫂和定期保洁状态为服务中
        if (GoodsTypeEnum.CODE0700.getValue().equals(orderUp.getGoodsTypeCode())) {
            orderUp.setPayStatus(4);
        } else {
            orderUp.setPayStatus(3);
        }

        orderUp.setPayType(1);
        orderUp.setOrderStatus(1);
        orderUp.setPayDate(new Date());
        orderUp.setUpdateTime(new Date());
        orderUp.setRemark("微信支付成功回调，改变订单状态");
        orderService.updateByPrimaryKeySelective(orderUp);
    }
}
